package it.unisannio.pgridporting;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import p2p.basic.GUID;
import p2p.basic.Key;
import p2p.basic.Message;
import p2p.basic.P2P;
import p2p.basic.P2PFactory;
import p2p.basic.Peer;
import p2p.basic.events.P2PListener;
import p2p.storage.DataItem;
import p2p.storage.Query;
import p2p.storage.Storage;
import p2p.storage.StorageFactory;
import p2p.storage.Type;
import p2p.storage.TypeHandler;
import p2p.storage.events.SearchListener;
import pgrid.Constants;
import pgrid.PGridHost;
import pgrid.RangeQuery;
import pgrid.core.storage.DataModifier;
import pgrid.core.storage.StorageManager;
import pgrid.interfaces.basic.PGridP2P;
import pgrid.interfaces.basic.PGridP2PFactory;
import pgrid.interfaces.storage.PGridStorageFactory;
import pgrid.network.protocol.ChallengeMessage;
import pgrid.network.protocol.DataModifierMessage;
import pgrid.network.protocol.ExchangeMessage;
import pgrid.network.protocol.GenericMessage;
import pgrid.network.protocol.InitMessage;
import pgrid.network.protocol.PeerLookupMessage;
import pgrid.network.protocol.QueryMessage;
import pgrid.network.protocol.RangeQueryMessage;
import pgrid.network.protocol.ReplicateMessage;
import pgrid.network.protocol.SearchPathMessage;
import test.SimpleTypeHandler;
import test.WaitingArea;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
 
public class PGridPorting extends Activity implements SearchListener, P2PListener{
	
	public static final int QUERY = 1;
	public static final int RANGE_QUERY = 0;
	public static final int BOOTSTRAP = 2;
	public static final int EXCHANGE = 3;
	public static final int EXCHANGE_INVITATION = 4;
	public static final int GENERIC = 5;
	public static final int INIT = 6;
	public static final int SEARCH_PATH = 7;
	public static final int LOOK_UP = 8;
	public static final int CHALLENGE = 9;
	public static final int REPLICATE = 10;
	public static final int DATA_MODIFIER = 11;
	
	
    /** Called when the activity is first created. */
	
	private TextView tv;
	private P2PFactory p2pFactory;
	private P2P p2p;
	private StorageFactory storageFactory;
	private Storage storage;
	
    @Override
    public void onCreate(Bundle icicle) {
    	super.onCreate(icicle);
    	
    	tv = new TextView(this);
        setContentView(tv);
        tv.append("Test avviato \n");
        
        /*
         *Con queste 2 righe creiamo delle directory sul dispositivo.
         * Sembra l'unica strada per poter creare directory. Una volta che la
         * directory è stata creata con questo metodo, ci si può scrivere
         * utilizzando la normale api relativa ai file, che altrimenti non funziona.
         */
        File dir = this.getDir("myDir", Activity.MODE_PRIVATE);
        File dataDir = this.getDir("data", Activity.MODE_PRIVATE);

        try {
			Constants.DATA_DIR = dataDir.getCanonicalPath() + System.getProperty("file.separator");
			tv.append("DATA_DIR defined \n");
		} catch (IOException e3) {
			tv.append("DATA_DIR defined error");
			e3.printStackTrace();
		}
      
        P2PFactory factory = PGridP2PFactory.sharedInstance();
		//tv.append("Factory created \n");
		
		/**
		 * Debug
		 */
		Log.v("MyMessage", "Factory created");
	
        String dirPath = null;
		try {
			dirPath = dir.getCanonicalPath();
		} catch (IOException e1) {
			tv.append("Problems in dir creation");
			e1.printStackTrace();
		}
		
		Properties properties = new Properties();
		//properties.setProperty(pgrid.Properties.BOOTSTRAP_HOSTS, "192.168.70.132:1806");
		properties.setProperty(PGridP2P.PROP_VERBOSE_MODE, "true");
		properties.setProperty(PGridP2P.PROP_LOCAL_PORT, "5000");
		properties.setProperty(pgrid.Properties.INIT_EXCHANGES, "true");
		try {
			PGridP2P.IP_HOST = InetAddress.getByAddress(new byte [] {(byte) 192, (byte) 168, (byte) 70, (byte) 102});
		} catch(UnknownHostException e) {
			e.printStackTrace();
		}
	
		/* 
		 * Con queste properties facciamo in modo che il file 
		 * di property e quello di log di pgrid finiscano in 
		 * una delle cartelle create.
		 */
		properties.setProperty(PGridP2P.PROP_PROPERTY_FILE, dirPath+System.getProperty("file.separator") + "myPropFile.prop");
		properties.setProperty(PGridP2P.PROP_LOG_FILE, dirPath+System.getProperty("file.separator") + "myLogFile.log");
		
		Logger log = Logger.getLogger("PGrid");
		log.setLevel(Level.OFF);
		
		//String runMode = "Contacts_Searcher";
		String runMode = "Contacts_Adder";
		
		
		// BOOTSTRAP TEST
//		P2P service1 = factory.createP2P(properties);
//		service1.addP2PListener(new P2PListener() {
//		
//					public void newMessage(Message message, Peer origin) {
//						//				Log.v("MyMessage", "Remote message received: " + new String(((GenericMessage)message).getData()).trim());
//						//				tv.append("New message: " + new String(((GenericMessage)message).getData()).trim());
//						Log.v("MyMessage", "Remote message received: "
//								+ message.getData());
//					}
//				});
//				Log.v("MyMessage","In attesa"); 
//		StorageFactory storageFactory1 = PGridStorageFactory.sharedInstance();
//		Storage storage1 = storageFactory.createStorage(service1);
//		WaitingArea.waitTillSignal(300 * 1000);
		///////////////////
		
		//String runMode = "Contacts_Searcher";
	
//		P2P service1 = factory.createP2P(properties);
//		service1.addP2PListener(new P2PListener() {
//
//			public void newMessage(Message message, Peer origin) {
//				//				Log.v("MyMessage", "Remote message received: " + new String(((GenericMessage)message).getData()).trim());
//				//				tv.append("New message: " + new String(((GenericMessage)message).getData()).trim());
//				Log.v("MyMessage", "Remote message received: "
//						+ message.getData());
//			}
//		});
		//Log.v("MyMessage","In attesa"); 
		//WaitingArea.waitTillSignal(300 * 1000);
		
		if (runMode.equals("Debug")) {
			P2P service = factory.createP2P(properties);
			StorageFactory storageFactory = PGridStorageFactory
					.sharedInstance();
			Storage storage = storageFactory.createStorage(service);
			tv.append("Service created \n");
			/**
			 * Debug
			 */
			Log.v("MyMessage", "Service created");
			Peer bootstrap = null;
			try {
				bootstrap = factory.createPeer(InetAddress
						.getByAddress(new byte[] { (byte) 10, (byte) 252,
								(byte) 37, (byte) 96, }), 5000);

				Log.v("MyMessage", "Bootstrap created");

			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
			Peer myPeer = service.getLocalPeer();
			tv.append("Info peer: " + myPeer.getIP() + ":" + myPeer.getPort());
			String s = "ciao!";
			service.addP2PListener(new P2PListener() {

				public void newMessage(Message message, Peer origin) {
					//				Log.v("MyMessage", "Remote message received: " + new String(((GenericMessage)message).getData()).trim());
					//				tv.append("New message: " + new String(((GenericMessage)message).getData()).trim());
					Log.v("MyMessage", "Remote message received: "
							+ message.getData());
				}
			});
			tv.append("Send and wait\n");
			//BOOTSTRAP
			//		Log.v("MyMessage", "Bootstrap joined");
			//		service.join(bootstrap);
			WaitingArea.waitTillSignal(30000);
			Type type = storageFactory.createType("SimpleType");
			TypeHandler handler = new SimpleTypeHandler(type);
			storageFactory.registerTypeHandler(type, handler);
			DataItem item = storageFactory.createDataItem(type,
					"MyPersonalDataItem1");
			DataItem item2 = storageFactory.createDataItem(type,
					"MyPersonalDataItem2");
			Collection items = new Vector();
			items.add(item);
			items.add(item2);
			Message msg;
			Peer otherPeer = null;
			try {
				otherPeer = factory.createPeer(InetAddress
						.getByAddress(new byte[] { (byte) 10, (byte) 252,
								(byte) 37, (byte) 96, }), 5000);
			} catch (UnknownHostException e1) {
				e1.printStackTrace();
			}
			int mode = PGridPorting.DATA_MODIFIER;
			switch (mode) {
			case PGridPorting.GENERIC:
				service.send(service.getLocalPeer(), factory.createMessage(s
						.getBytes()));
				break;
			case PGridPorting.BOOTSTRAP:
				break;
			case PGridPorting.QUERY:
				Query query = storageFactory.createQuery(type, "MyP");
				//storage.search(query, this);
				msg = new QueryMessage(query.getGUID(), query.getType(), query
						.getLowerBound(), query.getKeyRange().getMin(), 0, 0,
						(PGridHost) myPeer, 0, null);
				service.send(otherPeer, msg);
				break;
			case PGridPorting.RANGE_QUERY:
				query = storageFactory.createQuery(type, "MyP", "Myp");
				//storage.search(query, this);
				msg = new RangeQueryMessage(query.getGUID(), query.getType(),
						0 + 1, RangeQuery.MINMAX_ALGORITHM, query
								.getLowerBound(), query.getHigherBound(), query
								.getKeyRange(), 0, "100101111", 0,
						(PGridHost) myPeer);
				service.send(otherPeer, msg);
				break;
			case PGridPorting.INIT:
				//MAI UTILIZZATO
				msg = new InitMessage(myPeer.getGUID(), 4900);
				service.send(service.getLocalPeer(), msg);
				break;
			case PGridPorting.SEARCH_PATH:
				msg = new SearchPathMessage("100101111", 0);
				service.send(otherPeer, msg);
				break;
			case PGridPorting.EXCHANGE:
				//msg = new ExchangeMessage("100101111", 0);
				//service.send(otherPeer, msg);
				break;
			case PGridPorting.LOOK_UP:
				Key key = item.getKey();
				msg = new PeerLookupMessage(myPeer.getGUID(), key.toString(),
						PGridP2P.sharedInstance().getLocalHost(),
						PeerLookupMessage.ANY);
				service.send(otherPeer, msg);
				break;
			case PGridPorting.CHALLENGE:
				msg = new ChallengeMessage((pgrid.GUID) myPeer.getGUID(),
						ChallengeMessage.XML_CHALLENGE);
				service.send(otherPeer, msg);
				break;
			case PGridPorting.REPLICATE:
				msg = new ReplicateMessage(items);
				service.send(otherPeer, msg);
				break;
			case PGridPorting.DATA_MODIFIER:
				msg = new DataModifierMessage((pgrid.GUID) factory
						.generateGUID(), item.getKey(), DataModifier.DELETE,
						items);
				service.send(otherPeer, msg);
				//storage.delete(items);
				break;
			}
		}else if(runMode.equals("Contacts_Adder")){
	///////////
			P2P service = factory.createP2P(properties);
			StorageFactory storageFactory = PGridStorageFactory.sharedInstance();
			Storage storage = storageFactory.createStorage(service);
			
			// Get an instance of the P2PFactory
			//p2pFactory = PGridP2PFactory.sharedInstance();
			Log.v("MyMessage", "Acquired P-Grid factory reference. ");
		
			//tv.append("Service created \n");
			/**
			 * Debug
			 */
			Log.v("MyMessage", "Service created");
			Peer bootstrap = null;
			try {
				bootstrap = factory.createPeer(InetAddress.getByAddress(new byte[] { (byte) 192, (byte) 168, (byte) 70, (byte) 102}), 5000);

				Log.v("MyMessage", "Bootstrap created");

			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
			Peer myPeer = service.getLocalPeer();
			//tv.append("Info peer: " + myPeer.getIP() + ":" + myPeer.getPort());
			String s = "ciao!";
			service.addP2PListener(new P2PListener() {

				public void newMessage(Message message, Peer origin) {
					//				Log.v("MyMessage", "Remote message received: " + new String(((GenericMessage)message).getData()).trim());
					//				tv.append("New message: " + new String(((GenericMessage)message).getData()).trim());
					Log.v("MyMessage", "Remote message received: "
							+ message.getData());
				}
			});
			tv.append("Send and wait\n");
			//BOOTSTRAP
					
			//WaitingArea.waitTillSignal(100 * 1000);
			service.join(bootstrap);
			Log.v("MyMessage", "Bootstrap joined");
			//////////
			
			
			// Get an instance of the P2P object, aka P-Grid
//			p2p = p2pFactory.createP2P(properties);
//			Log.v("MyMessage", "Created a P2P instance. ");
//			storageFactory = PGridStorageFactory.sharedInstance();
//			Log.v("MyMessage", "Storage factory reference acquired. ");
//		
//		
//			// Get an instance of the Storage object, aka PGridStorage
//			storage = storageFactory.createStorage(p2p);
//			Log.v("MyMessage", "Storage instance acquired. ");
			// creating and registering data type.
			Type type = storageFactory.createType("ContactType");
			TypeHandler handler = new SimpleTypeHandler(type);
			storageFactory.registerTypeHandler(type, handler);
			Log.v("MyMessage", "TypeHandler registered");
			
			Vector items = new Vector();
			DataItem item;
			Contact contact= new Contact();
			contact.setId("Andy Brown");
			contact.setPhoneNumber("+393938383921");
			contact.setEmail("andy@brown.it");
			contact.setAddress("Somewhere");
			item = storageFactory.createDataItem(type, contact.toString());
			Log.v("MyMessage", "DataItem created");
			Collection c=storage.getLocalDataItems();
			boolean alreadyInserted=false;
			Iterator it=c.iterator();
			while(it.hasNext()){
				DataItem currentItem=(DataItem) it.next();
				if(currentItem.equals(item))
						alreadyInserted=true;
			}
			
			if(alreadyInserted==false)
				items.add(item);
			storage.insert(items);
			Log.v("MyMessage", "Items inserted");
			
			Log.v("MyMessage", "Waiting for queries...");
			WaitingArea.waitTillSignal(160000);
		}else if(runMode.equals("Contacts_Searcher")){
			// Get an instance of the P2PFactory
			p2pFactory = PGridP2PFactory.sharedInstance();
			Log.v("MyMessage", "Acquired P-Grid factory reference. ");

			// Get an instance of the P2P object, aka P-Grid
			p2p = p2pFactory.createP2P(properties);
			Peer bootstrap=null;
			try {
				bootstrap= p2pFactory.createPeer(InetAddress.getByAddress(new byte[]{10,10,10,10}), 4900);
			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			Log.v("MyMessage", "Created a P2P instance. ");
			storageFactory = PGridStorageFactory.sharedInstance();
			Log.v("MyMessage", "Storage factory reference acquired. ");
			

			// Get an instance of the Storage object, aka PGridStorage
			storage = storageFactory.createStorage(p2p);
			Log.v("MyMessage", "Storage instance acquired. ");
			// creating and registering data type.
			Type type = storageFactory.createType("ContactType");
			TypeHandler handler = new SimpleTypeHandler(type);
			storageFactory.registerTypeHandler(type, handler);
			
			Query query=storageFactory.createQuery(type, "Andy");
			WaitingArea.waitTillSignal(1*120000);
			
			storage.search(query, this);
			//while(true);
		}

		
		
		
//		
		
//		boolean b = true;
//		while(true) {
//			if(b) {
//				Log.v("MyMessage", "while");
//				b = false;
//			}
//		}
			
/*		try {
			service.send(bootstrap, factory.createMessage(s.getBytes("ISO-8859-1")));
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		
    }

	public void newSearchResult(GUID guid, Collection results) {
		Log.v("MyMessage", "# newSearchResult: "+results.size());
		Log.v("MyMessage", "Search result received:");
		for (Iterator it = results.iterator(); it.hasNext();) {
			DataItem item = (DataItem)it.next();
			Log.v("MyMessage", item.getData().toString());
		}
	}

	public void noResultsFound(GUID guid) {
		Log.v("MyMessage", "# noResultsFound: "+guid);
		
	}

	public void searchFailed(GUID guid) {
		Log.v("MyMessage", "# searchFailed: "+guid);
		
	}

	public void searchFinished(GUID guid) {
		Log.v("MyMessage", "# searchFinished: "+guid);
		
	}

	public void searchStarted(GUID guid, String message) {
		Log.v("MyMessage", "# searchStarted: "+message);
		
	}

	/**
	 * Implementation of the P2PListener interface. Is invoked
	 * when a new message is received and this node is responsible
	 * for the message's destination key. Reassembles the message
	 * text and displays it on the command line.
	 */
	public void newMessage(Message message, Peer origin) {
		String str="";
		try {
			str = new String(message.getData(), "ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		tv.append("Received message: " + str + " from " + origin.toString()+"\n");
		Log.v("MyMessage", "Received message: " + str + " from " + origin.toString()+"\n");
	}

/*	public void newMessage(Message message, Peer origin) {
		tv.append("New mwssage \n" + message.toString());
		Log.v("MyMessage", message.toString());
	}*/
 
}